using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Text;
using GMID = System.Int64;

namespace Cobalt75.GreyMatter
{
    public class GMParameterRelationDictionary<TKey, TValue> : IDictionary<TKey, TValue>
        where TValue : GMTable
    {
        #region IDictionary Implmentation
        public bool ContainsKey(TKey key)
        {
            return Keys.Contains(key);
        }
        public void Add(TKey key, TValue value)
        {
            pParentObject.Communicator.CreateLink(pParentObject, value, key);
        }
        public bool Remove(TKey key)
        {
            TValue value = this[key];
            pParentObject.Communicator.DeleteLink(pParentObject, value, key);
            return true;
        }
        public bool TryGetValue(TKey key, out TValue value)
        {
            if (ContainsKey(key))
            {
                value = this[key];
                return (true);
            }
            value = null;
            return (false);
        }
        public void Snapshot()
        {
            pQueryKeys.SetParameter("parentID", pParentObject.Id);
            pSnapShotKeys = pParentObject.Communicator.SelectParameters<TKey>(pQueryKeys);
         
            pSnapShotValues = new TValue[pSnapShotKeys.Length];

            for (int i = 0; i < pSnapShotKeys.Length; i++)
            {
                pSnapShotValues[i] = this[pSnapShotKeys[i]];
            }
            pSnapshotTicks = pParentObject.Communicator.GetTableSync(pQuery.pTables.ToArray());
            pSnapshotLinkTicks = pParentObject.Communicator.GetTableSync(pLinkTable.Name);


        }
        TKey[] pSnapShotKeys = null;
        TValue[] pSnapShotValues = null;
        public ICollection<TKey> Keys
        {
            get
            {
                if (pSnapShotKeys == null || ListIsOutdated())
                {
                    Snapshot();
                }

                return new ReadOnlyCollection<TKey>(pSnapShotKeys);
            }
        }
        public ICollection<TValue> Values
        {
            get
            {

                if (pSnapShotKeys == null || ListIsOutdated())
                {
                    Snapshot();
                }

                return new ReadOnlyCollection<TValue>(pSnapShotValues);
            }
        }
        #endregion
        #region ICollection Implementation
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            Add(item.Key, item.Value);
        }
        public void Clear()
        {
            if (pSnapShotKeys == null || ListIsOutdated())
            {
                Snapshot();
            }
            for (int i = 0; i < pSnapShotKeys.Length; i++)
            {
                Remove(pSnapShotKeys[i]);
            }
        }
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            if (ContainsKey(item.Key))
            {
                if (this[item.Key] == item.Value)
                {
                    return true;
                }
            }
            return false;
        }
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            if (pSnapShotKeys == null || ListIsOutdated())
            {
                Snapshot();
            }
            for (int i = 0; i < pSnapShotKeys.Length; i++)
            {
                array[i] = new KeyValuePair<TKey, TValue>(pSnapShotKeys[i], pSnapShotValues[i]);
            }
        }
        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return Remove(item.Key);
        }
        public int Count
        {
            get
            {
                return Keys.Count;
            }
        }
        public bool IsReadOnly
        {
            get
            {
                return (false);
            }
        }

        #endregion
        #region IEnumerable implementation


        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {

            if (pSnapShotKeys == null || ListIsOutdated())
            {
                Snapshot();
            }
            for (int i = 0; i < pSnapShotKeys.Length; i++)
            {
                yield return new KeyValuePair<TKey, TValue>(pSnapShotKeys[i], pSnapShotValues[i]);
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion
        
        GMTable pParentObject;
        GMLinkTable pLinkTable;
        GMQuery pQuery;
        GMQuery pQueryKeys;
        GMRelationCaching pCaching = GMRelationCaching.Updating;
        Dictionary<TKey, TValue> pCache = new Dictionary<TKey, TValue>();
        Dictionary<TKey, GMID> pIDCache = new Dictionary<TKey, GMID>();
        Dictionary<TKey, Int64> pListTicks = new Dictionary<TKey, Int64>();
        Int64 _LinkTicks = 0;
        Int64 pSnapshotTicks = 0;
        Int64 pSnapshotLinkTicks = 0;

        public GMParameterRelationDictionary(GMTable parentObject)
        {
            pParentObject = parentObject;
            pQuery = pParentObject.Communicator.RelationLookupQuery(parentObject.GetType(), typeof(TValue), typeof(TKey));
            pQueryKeys = pParentObject.Communicator.RelationLookupQueryAllKeys(parentObject.GetType(), typeof(TValue), typeof(TKey));
            pLinkTable = pParentObject.Communicator.LinkTableFor(parentObject.GetType(), typeof(TValue), typeof(TKey));
        }
        public GMQuery Query
        {
            get
            {
                return pQuery;
            }
        }
        public GMRelationCaching Caching
        {
            get
            {
                return (pCaching);
            }
            set
            {
                if (value == GMRelationCaching.Snapshot)
                {
                    throw (new GMException("Singluar relations cannot have snapshots"));
                }
                pCaching = value;

                pCache = new Dictionary<TKey, TValue>();
            }
        }
        void GetList(TKey Key)
        {
            if (pIDCache.ContainsKey(Key))
            {
                pIDCache.Remove(Key);
            }
            if (pCache.ContainsKey(Key))
            {
                pCache.Remove(Key);
            }
            if (pListTicks.ContainsKey(Key))
            {
                pListTicks.Remove(Key);
            }
            pQuery.SetParameter("parentID", pParentObject.Id);
            pQuery.SetParameter("indexID", Key.ToString());
            pIDCache.Add(Key, pParentObject.Communicator.SelectIDs(pQuery)[0]);
            pListTicks.Add(Key, pParentObject.Communicator.GetTableSync(pQuery.pTables.ToArray()));
            _LinkTicks = pParentObject.Communicator.GetTableSync(pLinkTable.Name);

        }
        public bool ListIsOutdated(TKey key)
        {
            Int64 newlTicks = pParentObject.Communicator.GetTableSync(pLinkTable.Name);
            if (newlTicks > _LinkTicks)
            {

                return (true);
            }
            Int64 newTicks = pParentObject.Communicator.GetTableSync(pQuery.pTables.ToArray());
            if (newTicks > pListTicks[key])
            {
                return (true);
            }
            return (false);

        }
        public bool ListIsOutdated()
        {
            Int64 newlTicks = pParentObject.Communicator.GetTableSync(pLinkTable.Name);
            if (newlTicks > pSnapshotLinkTicks)
            {

                return (true);
            }
            Int64 newTicks = pParentObject.Communicator.GetTableSync(pQuery.pTables.ToArray());
            if (newTicks > pSnapshotTicks)
            {
                return (true);
            }
            return (false);

        }
        public TValue this[TKey key]
        {
            get
            {
                if (!pListTicks.ContainsKey(key))
                {
                    GetList(key);
                }
                else if (ListIsOutdated(key) && Caching == GMRelationCaching.Updating)
                {
                    GetList(key);
                }
                if (!pCache.ContainsKey(key))
                {
                    pCache[key] = pParentObject.Communicator.SelectFromId<TValue>(pIDCache[key], true);
                }
                return (pCache[key]);

            }
            set
            {
                throw (new NotSupportedException());
            }
        }
    
    }
    
}
